package server

import (
	"errors"
	"log/slog"
	"net"
	"net/http"
	"os"
	"runtime/debug"
	"server/internal/core/constant"
	"strings"

	"github.com/gin-gonic/gin"
)

func Recover(log *slog.Logger) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					var se *os.SyscallError
					if errors.As(ne, &se) {
						seStr := strings.ToLower(se.Error())
						if strings.Contains(seStr, "broken pipe") ||
							strings.Contains(seStr, "connection reset by peer") {
							brokenPipe = true
						}
					}
				}
				if brokenPipe {
					// If the connection is dead, we can't write a status to it.
					log.WarnContext(ctx, "broken pipe", "error", err)
					ctx.Error(err.(error)) //nolint: errcheck
					ctx.Abort()
				} else {
					log.ErrorContext(ctx, "panic recovered", "error", err, "stack", debug.Stack())
					ctx.JSON(http.StatusInternalServerError, &ResponseData{
						Code:    http.StatusInternalServerError,
						Message: "An unexpected error occurred",
						Data:    nil,
						TraceID: ctx.GetString(constant.TraceIDKeyType{}),
					})
					ctx.Abort()
				}
			}
		}()
		ctx.Next()
	}
}
